C++11 新特性概述
课程目标
- 了解C++11相对于C++98的新特性
- 通过示例理解这些新特性的实际应用与意义
1. 自动类型推导 - auto
1.1 简介
- 使用
auto
关键字让编译器推断变量类型。
1.2 示例
cpp
auto i = 5; // int
auto d = 3.14; // double
auto str = "C++11"; // const char*
// 复杂类型推导
auto vec = std::vector<int>{1, 2, 3};
1.3 意义
- 减少冗余代码,提高可读性,尤其是对于复杂类型如迭代器。
2. Lambda 表达式
2.1 简介
- 定义匿名函数对象的能力,直接在需要使用函数的地方创建函数。
2.2 示例
cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
// 定义一个简单的lambda表达式,它接受一个参数并打印它
auto print = [](int n){ std::cout << n << " "; };
// 创建一个vector并使用lambda打印每个元素
std::vector<int> numbers = {5, 2, 9, 1, 5, 6};
std::for_each(numbers.begin(), numbers.end(), print);
std::cout << "\n";
// 使用lambda进行条件过滤,捕获外部变量
int threshold = 4;
std::vector<int> filtered;
std::copy_if(numbers.begin(), numbers.end(),
std::back_inserter(filtered),
[threshold](int x) { return x > threshold; });
// 打印过滤后的结果
std::cout << "Numbers greater than " << threshold << ": ";
std::for_each(filtered.begin(), filtered.end(), print);
std::cout << "\n";
// 嵌套lambda - 计算每个元素的平方,然后求和
int sumOfSquares = 0;
std::for_each(numbers.begin(), numbers.end(),
[&sumOfSquares](int x) {
sumOfSquares += [&x] {
int square = x * x;
return square;
}(); // 立即调用lambda计算平方
});
std::cout << "Sum of squares: " << sumOfSquares << std::endl;
// Lambda作为比较函数用于排序
std::sort(numbers.begin(), numbers.end(),
[](int a, int b) { return a > b; }); // 降序排序
std::cout << "Sorted in descending order: ";
std::for_each(numbers.begin(), numbers.end(), print);
std::cout << "\n";
return 0;
}
2.3 意义
- 增强了函数作为参数的使用场景,使得代码更灵活、更简洁。
3. 右值引用与移动语义
3.1 简介
- 引入了右值引用(
&&
),允许实现移动语义,避免不必要的拷贝。
3.2 示例
cpp
#include <iostream>
#include <cstring>
#include <utility> // for std::move
class MyString {
private:
char* m_data;
size_t m_size;
public:
// 常规构造函数
MyString(const char* str = nullptr) {
m_size = (str ? std::strlen(str) : 0);
m_data = new char[m_size + 1];
if (str) std::strcpy(m_data, str);
else m_data[0] = '\0';
std::cout << "Constructed: " << m_data << std::endl;
}
// 拷贝构造函数
MyString(const MyString& other) {
m_size = other.m_size;
m_data = new char[m_size + 1];
std::strcpy(m_data, other.m_data);
std::cout << "Copy constructed: " << m_data << std::endl;
}
// **移动构造函数**
MyString(MyString&& other) noexcept : m_data(nullptr), m_size(0) {
m_data = other.m_data; // 直接"steal" other的资源
m_size = other.m_size;
other.m_data = nullptr; // other不再拥有这些资源
other.m_size = 0;
std::cout << "Move constructed: " << m_data << std::endl;
}
// 析构函数
~MyString() {
delete[] m_data;
}
// **移动赋值操作符**
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] m_data; // 释放现有资源
m_data = other.m_data;
m_size = other.m_size;
other.m_data = nullptr;
other.m_size = 0;
}
std::cout << "Move assignment: " << m_data << std::endl;
return *this;
}
// 普通赋值操作符(简化版,不考虑自赋值问题)
MyString& operator=(const MyString& other) {
if(this != &other) {
delete[] m_data;
m_size = other.m_size;
m_data = new char[m_size + 1];
std::strcpy(m_data, other.m_data);
std::cout << "Copy assignment: " << m_data << std::endl;
}
return *this;
}
void print() const {
std::cout << (m_data ? m_data : "nullptr") << std::endl;
}
};
// 辅助函数,用于展示右值引用
MyString createString() {
return MyString("Temporary String");
}
int main() {
MyString str1("Hello");
MyString str2(str1); // 拷贝构造
str1.print(); // 验证str1未改变
MyString str3 = createString(); // 移动构造,因为createString()返回一个临时对象(右值)
str3.print();
MyString str4;
str4 = std::move(str3); // 移动赋值, str3将不再拥有其资源
str4.print();
str3.print(); // str3现在应该指向nullptr
return 0;
}
3.3 意义
- 提高性能,特别是对于含有大量资源的对象。
4. 标准库的改进
4.1 智能指针
std::shared_ptr
,std::unique_ptr
,std::weak_ptr
4.2 示例
cpp
#include <iostream>
#include <memory>
#include <vector>
class Resource {
public:
Resource() {
std::cout << "Resource acquired" << std::endl;
}
~Resource() {
std::cout << "Resource destroyed" << std::endl;
}
void use() {
std::cout << "Resource is being used." << std::endl;
}
};
// 函数返回 shared_ptr
std::shared_ptr<Resource> createResource() {
// 使用 std::make_shared 比直接使用 new 更为高效
return std::make_shared<Resource>();
}
void shareResource(std::shared_ptr<Resource> res) {
if(res)
res->use();
// 'res' 超出作用域时,它的引用计数自动减1
}
int main() {
{
std::shared_ptr<Resource> mainRes;
{
// 创建 shared_ptr 对象
auto res = createResource();
res->use();
// 复制 shared_ptr, 增加引用计数
mainRes = res;
// 传递 shared_ptr
shareResource(res);
} // 这里 res 超出作用域,但资源未被销毁,因为 mainRes 还在使用
mainRes->use();
} // mainRes 超出作用域,引用计数变为0,资源被销毁
// 使用 vector 来管理多个 shared_ptr
std::cout << "\nDemonstrating shared_ptr in a vector:\n" << std::endl;
std::vector<std::shared_ptr<Resource>> resources;
for (int i = 0; i < 3; ++i) {
resources.push_back(std::make_shared<Resource>());
}
for (auto &r : resources) {
r->use();
}
// 离开这个作用域时,所有 shared_ptr 被销毁,相应的资源也被释放
return 0;
}
4.3 意义
- 自动内存管理,减少内存泄漏的风险。
5. 多线程支持
5.1 简介
- 引入
<thread>
,<mutex>
,<future>
等支持多线程编程。
5.2 示例
cpp
#include <thread>
#include <chrono>
void task() {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "Thread done" << std::endl;
}
std::thread t(task);
t.join();
5.3 意义
- 提供了对并发编程的原生支持,简化了多线程程序的编写。
6. 其他重要特性
统一初始化:使用
{}
进行初始化。decltype:获取变量或表达式类型。
cpp#include <iostream> #include <vector> #include <typeinfo> int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; auto it = vec.begin(); // it 的类型为 std::vector<int>::iterator decltype(it) it2 = vec.end(); // it2 的类型也被推导为 std::vector<int>::iterator std::cout << "Type of it: " << typeid(it).name() << std::endl; std::cout << "Type of it2: " << typeid(it2).name() << std::endl; return 0; }
nullptr:一个表示空指针的关键字。
Range-based for loop:更简洁的遍历容器。
6.1 示例(Range-based for loop)
cpp
std::vector<int> vec = {1, 2, 3, 4, 5};
for (const auto &value : vec) {
std::cout << value << ' ';
}
6.2 意义
- 增强代码的表达力和安全性。
课堂练习
- 编写一个使用
auto
和decltype
的程序。 - 实现一个简单的lambda函数来排序一个vector。
- 使用右值引用实现一个自己的字符串类。
总结回顾
- C++11为现代C++编程带来了大量的便利和性能提升。
- 通过这些特性,可以写出更安全、更高效的代码。
作业:
创建一个类
FileManager
,该类使用std::shared_ptr
来管理std::fstream
对象。提供方法来打开文件,读取文件内容,写入文件,并确保在类销毁时文件被正确关闭。cppclass FileManager { public: // 构造函数、析构函数等 private: std::shared_ptr<std::fstream> fileStream; }; int main() { FileManager fm("example.txt"); // 演示文件操作 }
参考实现如下:
cpp
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
class FileManager {
public:
explicit FileManager(const std::string& filename) : filename_(filename) {
openFile();
}
~FileManager() {
if(fileStream_ && fileStream_->is_open()) {
fileStream_->close();
}
}
void openFile() {
fileStream_ = std::make_shared<std::fstream>(filename_, std::ios::in | std::ios::out | std::ios::app);
if(!fileStream_->is_open()) {
throw std::runtime_error("Could not open the file: " + filename_);
}
}
std::string readContent() {
if(!ensureFileOpen()) return "";
std::string content;
fileStream_->seekg(0, std::ios::end);
content.resize(fileStream_->tellg());
fileStream_->seekg(0, std::ios::beg);
fileStream_->read(&content[0], content.size());
return content;
}
void writeContent(const std::string& content) {
if(!ensureFileOpen()) return;
fileStream_->seekp(0, std::ios::end);
*fileStream_ << content;
}
private:
bool ensureFileOpen() {
if(!fileStream_ || !fileStream_->is_open()) {
openFile();
}
return fileStream_->is_open();
}
std::string filename_;
std::shared_ptr<std::fstream> fileStream_;
};
int main() {
try {
// 创建FileManager对象
FileManager fm("example.txt");
fm.writeContent("Hello, C++11 World!\n");
std::string content = fm.readContent();
std::cout << "File content:\n" << content << std::endl;
fm.writeContent("Adding more content.");
content = fm.readContent();
std::cout << "Updated file content:\n" << content << std::endl;
} catch(const std::exception& e) {
std::cerr << "Exception caught: " << e.what() << std::endl;
}
return 0;
}